#include "head.h"
#include <openssl/md5.h>
#define BUFFER_SIZE 4096


typedef struct file_train_s {
    ssize_t file_size;
    char name[25];
    unsigned char hash[MD5_DIGEST_LENGTH];
}file_train_t;

static void calculate_file_md5(int file_fd, unsigned char output[MD5_DIGEST_LENGTH]) {
    MD5_CTX md5Context;
    unsigned char buffer[4096];
    size_t bytesRead;

    // 初始化 MD5 上下文
    MD5_Init(&md5Context);

    // 读取文件并更新 MD5 哈希值
    while ((bytesRead = read(file_fd, buffer, sizeof(buffer))) != 0) {
        MD5_Update(&md5Context, buffer, bytesRead);
    }

    // 计算最终的 MD5 哈希值
    MD5_Final(output, &md5Context);

}


//接收文件，参数名cmd_argv为客户端上传的参数，创建文件的路径为cur_path;
int server_upload(int client_fd, char* cmd_argv, char *cur_path) {
    // 拼接文件的打开路劲
    char file_path[200] = { 0 };
    char *file_name = cmd_argv;
    memcpy(file_path, cur_path, strlen(cur_path));
    strcat(file_path, "/");
    strcat(file_path, file_name);

    int file_fd = open(file_path, O_RDONLY);

    //获得文件大小
    struct stat stat_file;
    bzero(&stat_file, sizeof(stat_file));
    fstat(file_fd, &stat_file);
    ssize_t file_size = stat_file.st_size;

    //获得文件MD5值
    unsigned char hash[MD5_DIGEST_LENGTH];
    calculate_file_md5(file_fd, hash);

    //将文件信息放入数据传输头部
    file_train_t file_info;
    bzero(&file_info, sizeof(file_info));

    file_info.file_size = file_size;
    memcpy(file_info.name, file_name, strlen(file_name));
    memcpy(file_info.hash, hash, MD5_DIGEST_LENGTH);

    //向客户端发送文件信息
    int res_send = send(client_fd, &file_info, sizeof(file_info), MSG_NOSIGNAL);
    if (res_send == -1) {
        printf("客户端断开连接\n");
        close(file_fd);
        close(client_fd);
        return -1;
    }
    

    //等待接收客户端发送的偏移量
    off_t offset;
    int res_recv = recv(client_fd, &offset, sizeof(off_t), MSG_WAITALL);
    ERROR_CHECK(res_recv, -1, "recv");
    //客户端断开连接， 退出程序
    if (res_recv == 0) {
        printf("客户端断开连接\n");
        close(file_fd);
        close(client_fd);
        return -1;
    }

    char *file_map = (char *)mmap(NULL, file_size, PROT_READ, MAP_SHARED, file_fd, 0);

    //开始传输文件数据
    while (offset < file_size) {
        ssize_t bytes_to_send = (file_size - offset > BUFFER_SIZE) ? BUFFER_SIZE : file_size - offset; 
        ssize_t bytes_sent = send(client_fd, file_map + offset, bytes_to_send, MSG_NOSIGNAL);
        if(bytes_sent == -1) {
            printf("客户端终端传输\n");
            close(file_fd);
            close(client_fd);
            return -1;
        }

        offset += bytes_sent;
    }

    munmap(file_map, file_size);
    close(file_fd);
    printf("server_upload: 100 \n");
    return 0;
}



int server_download(int client_fd, char* cmd_argv, char *cur_path) {
    //先接收客户端发送的携带文件信息的头部
    file_train_t file_info;
    bzero(&file_info, sizeof(file_info));
    recv(client_fd, &file_info, sizeof(file_train_t), MSG_WAITALL);

    //拼接绝对路径
    char write_path[40] = { 0 };
    memcpy(write_path, cur_path, strlen(cur_path));
    strcat(write_path, "/");
    strcat(write_path, file_info.name);
    printf("write_path = %s\n", write_path);
    printf("file_info.file_size = %ld\n", file_info.file_size);



    // 打开文件，用于写入接收到的数据
    int file_fd = open(write_path, O_WRONLY | O_CREAT, 0644);
    if (file_fd == -1) {
        perror("File open failed");
        close(client_fd);
        return 1;
    }

    char buffer[BUFFER_SIZE];
    ssize_t bytes_received;

    struct stat stat_file;
    bzero(&stat_file, sizeof(stat_file));
    fstat(file_fd, &stat_file);
    off_t offset = stat_file.st_size;

    //发送偏移量发送给客户端
    int res_send = send(client_fd, &offset, sizeof(off_t), MSG_NOSIGNAL);
    ERROR_CHECK(res_send, -1, "send");


    // 移动到文件的指定偏移量
    lseek(file_fd, offset, SEEK_SET);

    ssize_t bytes_to_received;
    // 从服务器接收数据并写入文件
    while (offset < file_info.file_size) {
        bytes_to_received = (file_info.file_size - offset > BUFFER_SIZE) ? BUFFER_SIZE : file_info.file_size - offset;
        bytes_received = recv(client_fd, buffer, bytes_to_received, MSG_WAITALL);

        write(file_fd, buffer, bytes_received);

        offset += bytes_received;
    }


    close(file_fd);
    return 0;
}
